package org.bintrotter.apeer;

import org.bintrotter.ClientInfo;
import org.bintrotter.afiles.Files;
import org.bintrotter.contentmanager.Block;
import org.bintrotter.contentmanager.ContentManager;

public class BlockTransit {

	public final int piece;
	
	private ContentManager queuedBlocks;
	private ContentManager transitBlocks;

	private int m_completed;
	private int m_inTransit;
	private int m_queued;
	private final int m_blocks;
	private int m_nextToQueue;
	private Files m_files;
	
	public BlockTransit(int piece, Files  m_files) {
		
		this.piece = piece;
		this.m_files = m_files;
			
		m_blocks = determineBlocks();
		
		m_nextToQueue = 0;
		
		int depth=1;
		while(Math.pow(2, depth)-1 < m_blocks)
			++depth;
		queuedBlocks = new ContentManager(depth, Block.Set);
		transitBlocks = new ContentManager(depth);
		m_completed = 0;
		m_inTransit = 0;
		m_queued = m_blocks;
	}
	
	private void onBegin(int Block) {
		
		try {
			
			if(queuedBlocks.isSet(Block) && !transitBlocks.isSet(Block)) {
				++m_inTransit;
				--m_queued;
			}
			
			queuedBlocks.unset(Block);
			transitBlocks.set(Block);
		}
		catch(Exception e){} 
	}
	
	public void onComplete(int piece, int begin, int length) {
		
		if(this.piece != piece)
			return;
		
		try {
			
			int blocklength = ClientInfo.instance().staticRequestLength();
			int Block = begin/blocklength;
			
			if(queuedBlocks.isSet(Block) || transitBlocks.isSet(Block)) {
				++m_completed;
				--m_inTransit;
			}
			
			queuedBlocks.unset(Block);
			transitBlocks.unset(Block);
		}
		catch(Exception e){} 
	}
	
	public Request next() {
		
		onBegin(m_nextToQueue);
		
		int rqsize = ClientInfo.instance().staticRequestLength();
		
		Request rq = new Request(piece, m_nextToQueue * rqsize, determineBlockLength(m_nextToQueue));
		
		m_nextToQueue++;
		
		return rq;
	}
	
	public int completed() {
		
		return m_completed;
	}
	
	public int inTransit() {
		
		return m_inTransit;
	}
	
	public int queued() {
		
		return m_queued;
	}
	
	private int determineBlockLength(int blockIndex) {
		
		long rqsize = ClientInfo.instance().staticRequestLength();
		
		if( blockIndex == m_blocks - 1) {
			
			long download = m_files.metafile.length();
			long piecelength = m_files.metafile.pieceLength();
			long piecestartaddress = piecelength * piece;
			long pieceendaddress = piecelength * (1+piece);
			if(pieceendaddress > download)
				pieceendaddress = download;
			piecelength = pieceendaddress - piecestartaddress;
			
			if(piecelength % rqsize == 0)
				return (int)rqsize;
			
			return (int)(piecelength % rqsize); 
		}
		
		return (int)(rqsize); 
	}
	
	private int determineBlocks() {
				
		if(piece == m_files.metafile.pieces()-1) {
			
			long download = m_files.metafile.length();
			long piecelength = m_files.metafile.pieceLength();
			long piecestartaddress = piecelength * piece;
			long lastpiecelength = download - piecestartaddress;
			long rqsize = ClientInfo.instance().staticRequestLength();
			
			if(lastpiecelength % rqsize == 0)
				return (int)(lastpiecelength / rqsize);
			return (int)(1 + (lastpiecelength / rqsize));	
		}
		
		return  m_files.metafile.blocksPerPiece();
	}
}

